package br.org.bertol.mestrado.engine.optimisation;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;

import br.org.bertol.mestrado.Verify;
import br.org.bertol.mestrado.engine.Objective;

public abstract class Unit implements Cloneable {
    /**
     */
    protected ArrayList<Integer>          route           = new ArrayList<Integer>();

    /**
     */
    protected EnumMap<Objective, Integer> height;

    /**
     */
    private boolean                       validPath;

    /**
     */
    private boolean                       dominated;

    /**
     */
    private int                           espacialPosition;

    /**
     */
    private int                           controlePermuta = 0;

    /**
     */
    private int                           x1              = 0;

    /**
     */
    private int                           x2              = 0;

    public Unit() {
        super();

        height = new EnumMap<Objective, Integer>(Objective.class);

        validPath = false;

        espacialPosition = 0;
    }

    public final boolean isValidPath() {
        return validPath;
    }

    public final void setValidPath(final boolean validPath) {
        this.validPath = validPath;
    }

    /**
     * @param route
     *            the route to set
     */
    @SuppressWarnings("unchecked")
    public final void setRoute(final ArrayList<Integer> route) {
        this.route = (ArrayList<Integer>) route.clone();
    }

    public final int routeLength() {
        return route.size();
    }

    /**
     * @param <T>
     * @param a
     * @return Um array contendo todos os elementos do caminho
     */
    public final <T> T[] toArray(final T[] a) {
        return route.toArray(a);
    }

    public final void setHeight(final EnumMap<Objective, Integer> objectives) {
        this.height = objectives.clone();
    }

    public final void setHeigtht(final Objective objective, final int height) {
        this.height.put(objective, height);
    }

    public final EnumMap<Objective, Integer> getHeight() {
        return height;
    }

    /**
     * @param objective
     * @param new_height
     */
    public final void addHeigtht(final Objective objective, final int new_height) {

        int oldHeigtht = height.get(objective);

        int newHeigtht = oldHeigtht + new_height;

        height.put(objective, newHeigtht);
    }

    /**
     * @param sum
     */
    public final void sumHeigtht(final EnumMap<Objective, Integer> sum) {

        // percorre todos os objetivos do problema adicionando novos
        // valores para os pesos
        for (final Objective objective : height.keySet())
            height.put(objective, height.get(objective) + sum.get(objective));
    }

    /**
     * @param e
     * @return true caso tenho ocorrido adi��o, falso caso contr�rio
     * @see java.util.List#contains(Object)
     */
    public final boolean addClass(final Integer e) {
        // evitar adicionar a mesma clase duas vezes no mesmo caminho
        if (route.contains(e)) {
            return false;
        }

        return route.add(e);
    }

    /**
     * @param index
     * @return true caso tenho ocorrido remo��o, falso caso contr�rio
     * @see java.util.List#remove(int)
     */
    public final boolean removeClass(final Integer index) {
        return route.remove(index);
    }

    public final ArrayList<Integer> getRoute() {
        return route;
    }

    public final void evaluateConstraint(final Verify verify) throws Exception {
        evaluateConstraint(verify, height.keySet().toArray(
                                                           new Objective[height
                .keySet()
                .size()]));
    }

    public final void evaluateConstraint(final Verify verify,
            final Objective... objectives) throws Exception {
        // verifica a preced�ncia
        verify.verifyStrongConstraint(this, true);

        // verifica se passou na verifica��o
        if (validPath) {
            // zera caminhos para execu��o da busca
            // heigtht = 0;
            for (final Objective objective : objectives) {
                height.put(objective, 0);
                verify.verifyWeekConstraint(this, true, objective);
            }
        }
    }

    public abstract void update();

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public Object clone() throws CloneNotSupportedException {

        final Unit tmp = (Unit) super.clone();

        tmp.route = (ArrayList<Integer>) this.route.clone();

        tmp.height = this.height.clone();

        return tmp;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        final StringBuilder buffer = new StringBuilder();

        buffer.append("Posi��o no espa�o: ").append("[")
                .append(
                        espacialPosition).append("] | ");

        buffer.append("      Valor");

        buffer.append(" f(").append(Objective.METODO).append(") : ");

        buffer.append("[").append(height.get(Objective.METODO)).append("] | ");

        buffer.append(" f(").append(Objective.ATRIBUTO).append(") : ");

        buffer.append("[").append(height.get(Objective.ATRIBUTO))
                .append("] | ");

        buffer.append("      Caminho encontrado : [");

        for (Integer element : route) {
            buffer.append(element).append(" ");
        }

        buffer.append("]");

        return buffer.toString();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((height == null) ? 0 : height.hashCode());
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Unit)) {
            return false;
        }
        Unit other = (Unit) obj;
        if (height == null) {
            if (other.height != null) {
                return false;
            }
        } else if (!height.equals(other.height)) {
            return false;
        }
        return true;
    }

    public final int getSpacialPosition() {
        return espacialPosition;
    }

    public final void setSpacialPosition(final int espacialPosition) {
        this.espacialPosition = espacialPosition;
    }

    /**
     * @param x
     */
    public final void setControlePermuta(final int x) {
        controlePermuta = x;
    }

    public final int getX1() {
        return x1;
    }

    public final void setX1(final int x1) {
        this.x1 = x1;
    }

    public final int getX2() {
        return x2;
    }

    public final void setX2(final int x2) {
        this.x2 = x2;
    }

    public final int getControlePermuta() {
        return controlePermuta;
    }

    public final void undoneMove() {
        Collections.swap(route, x2, x1);
    }

    public final boolean isDominated() {
        return dominated;
    }

    public final void setDominated(final boolean dominated) {
        this.dominated = dominated;
    }
}